# REST framework之序列化器

  • 创建models.py表结构

    from django.db import models
    
    class UserGroup(models.Model):
        title = models.CharField(max_length=32)
    
    class UserInfo(models.Model):
        user_type_choices = (
            (1, '普通用户'),
            (2, 'VIP'),
            (3, 'SVIP'),
        )
        user_type = models.IntegerField(choices=user_type_choices)
        username = models.CharField(max_length=32, unique=True)
        password = models.CharField(max_length=64)
        
        group = models.ForeignKey('UserGroup')  # 一对多
        roles = models.ManyToManyField('Role')  # 多对多
    
    
    class UserToken(models.Model):  # 用户储存用户的token
        user = models.OneToOneField(to='UserInfo')
        token = models.CharField(max_length=64)
    
    class Role(models.Model):
        title = models.CharField(max_length=32)
     
     # 执行数据库迁移,并进行填充数据
    

# 查询数据

# 使用原有的方法取出modles中的数据,并进行返回
from . import models
import json
class RoleView(APIView):  # 全局以配置了解析器
    def get(self,request,*args,**kwargs):    
        roles = models.Role.objects.all()
        ret = json.dumps(roles)  # 报错,因为json只能序列化python的数据类型,QuerySet是Django的数据类型
        return HttpResponse(ret)
    
 #解决方案:
class RoleView(APIView):  # 全局以配置了解析器
    def get(self,request,*args,**kwargs):    
        roles = models.Role.objects.all().values('id','title')
        roles = list(roles)  # 因为这里的values取出来的还是Queryset装的字典
        ret = json.dumps(roles, ensure_ascii=False)   # 通过这个参数可以防止传入的字符串进行ascii编码
        return HttpResponse(ret)
        
# 1、使用RESTframework来取数据(对Queryset列表进行序列化,多个使用many=True)
from . import models
import json
from rest_framework import serializers

class RoleSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    title = serializers.CharField()  # title必须和models中的字段进行对应

class RoleView(APIView):  # 全局以配置了解析器
    def get(self,request,*args,**kwargs):        
        roles = models.Role.objects.all()
        ser = RoleSerializer(instance=roles,many=True)  # many表示传入的数据有多条
        ret = json.dumps(ser.data, ensure_ascii=False)  # ser.data 表示从数据库中查出的对象role进行了Roleseralizer序列化后的字典
        return HttpResponse(ret) #[{"id": 1, "title": "医生"}, {"id": 2, "title": "学生"}]
 
--------------------------------------------------------------------
 # url.py文件中
urlpatterns = [
    url(r'(?P<version>[v1|v2]+)/users/$',views.UsersView.as_view(),name='uuu'),
    url(r'(?P<version>[v1|v2]+)/parser/$',views.ParserView.as_view()),
    url(r'(?P<version>[v1|v2]+)/roles/$',views.RoleView.as_view()),
]
  • 小提示:

      1、 roles = models.Role.objects.filter(title='医生')  # 当针对Queryset类型时必须配置many=True,即使只有一个对象
      2、 也就是说我们要取数据的时候,必须先写一个序列化类,字段要和相对应的models中的字段像对象,没有强硬的要求(在查询的时候)
    
# 2、对单独的表对象进行取数据(序列化单个对象时many=False)
class RoleSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    title = serializers.CharField()  # title必须和models中的字段进行对应

class RoleView(APIView):  # 全局以配置了解析器
    def get(self,request,*args,**kwargs):       
        roles = models.Role.objects.all().first()
        ser = RoleSerializer(instance=roles,many=True)  # many表示传入的数据有多条
        ret = json.dumps(ser.data, ensure_ascii=False)  # ser.data 表示从数据库中查出的对象role进行了Roleseralizer序列化后的有序字典
        return HttpResponse(ret)  #{"id": 1, "title": "医生"}
# 3、处理字段中含有choices选项显示对应的值
  • 原始状态我们可以取出的字段是数据库中储存的值,如下:我们想得到对应得值

    class UserInfoSerializer(serializers.Serializer): 
        xxxx = serializers.CharField(source='user_type')  # 配置source来字段字段,之后就可以任意该名
        username = serializers.CharField()
        password = serializers.CharField()
            
    class UserinfoView(APIView):
        def get(self, *args, **kwargs):
            users = models.UserInfo.objects.all()
            ser = UserInfoSerializer(instance=users, many=True)
            print(ser.data) #[OrderedDict([('username', '序号话'), ('password', '123')]), OrderedDict([('username', '民人接'), ('password', '123')])]
            ret = json.dumps(ser.data, ensure_ascii=False)
            return HttpResponse(ret)
            
      # [OrderedDict([('xxxx', '1'), ('username', '序号话'), ('password', '123')]), OrderedDict([('xxxx', '2'), ('username', '民人接'), ('password', '123')])]
    
    • 小提示:

       	之前我们说每个字段必须想对应models表中得每个字段,那是因为没有配置source选项,配置了后就可以重写自己得字段名了
      
  • 方案一:通过source配置相应字段得处理方式(source='get_字段_display')

    from rest_framework import serializers
    class UserInfoSerializer(serializers.Serializer):
        xxxx = serializers.CharField(source='user_type')
        ooo = serializers.CharField(source='get_user_type_display')  # 相似于models中的get_字段_display,取出相应字段名后调用get方法
        username = serializers.CharField()
        password = serializers.CharField()
            
    class UserinfoView(APIView):
        def get(self, *args, **kwargs):
            users = models.UserInfo.objects.all()
            ser = UserInfoSerializer(instance=users, many=True)
            print(ser.data) 
            ret = json.dumps(ser.data, ensure_ascii=False)
            return HttpResponse(ret)
        
       # [{"xxxx": "1", "ooo": "普通用户", "username": "序号话", "password": "123"}, {"xxxx": "2", "ooo": "VIP", "username": "民人接","password": "123"}]
    
# 4、处理关系管理字段(一对多)
  • 方案一:

    from rest_framework import serializers
    class UserInfoSerializer(serializers.Serializer):
        xxxx = serializers.CharField(source='user_type')
        ooo = serializers.CharField(source='get_user_type_display')  # 相似于models中的get_字段_display,取出相应字段名后调用get方法
        username = serializers.CharField()
        password = serializers.CharField()
        gp = serializers.CharField(source='group.title') # 通过点来获取值
        gp1 = serializers.CharField(source='group')
        
    class UserinfoView(APIView):
        def get(self, *args, **kwargs):
            users = models.UserInfo.objects.all()
            ser = UserInfoSerializer(instance=users, many=True)
            print(ser.data)
            ret = json.dumps(ser.data, ensure_ascii=False)
            return HttpResponse(ret)
            
     #[{"xxxx": "1", "ooo": "普通用户", "username": "序号话", "password": "123", "gp": "A组", "gp1": "UserGroup object"}, {"xxxx":"2", "ooo": "VIP", "username": "民人接", "password": "123", "gp": "B组", "gp1": "UserGroup object"}]  
    

# 5、处理多对多字段时
from rest_framework import serializers
class UserInfoSerializer(serializers.Serializer):
    xxxx = serializers.CharField(source='user_type')
    ooo = serializers.CharField(source='get_user_type_display')  # 相似于models中的get_字段_display,取出相应字段名后调用get方法
    username = serializers.CharField()
    password = serializers.CharField()
    gp = serializers.CharField(source='group.title') # 通过点来获取值
    gp1 = serializers.CharField(source='group')
    
    rls = serializers.CharField(source='roles.all')  # 多对多时,反向点all,返回的时Queryset列表,里面都都是对象
    
class UserinfoView(APIView):
    def get(self, *args, **kwargs):
        users = models.UserInfo.objects.all()
        ser = UserInfoSerializer(instance=users, many=True)
        print(ser.data)
        ret = json.dumps(ser.data, ensure_ascii=False)
        return HttpResponse(ret)
    
   # [{"xxxx": "1", "ooo": "普通用户", "username": "序号话", "password": "123", "gp": "A组", "gp1": "UserGroup object", "rls": "<QuerySet [<Role: Role object>, <Role: Role object>]>"}, {"xxxx": "2", "ooo": "VIP", "username": "民人接", "password":"123", "gp": "B组", "gp1": "UserGroup object", "rls": "<QuerySet [<Role: Role object>]>"}]
  • 小提示:

    1、通过source配置相应得字段,可以解决一对多(正向,一对一)和choices字段去值得问题,但是涉及到多对多时,就不能做到更详细得处理了,只能返回对象
    2、.all方法,只适用于关系管理对象字段上
    
  • # 更细力度得获取到多对多中,每个被关联对象得字段(通过SerializerMethodField)
    from rest_framework import serializers
    class UserInfoSerializer(serializers.Serializer):
        xxxx = serializers.CharField(source='user_type')
        ooo = serializers.CharField(source='get_user_type_display')  # 相似于models中的get_字段_display,取出相应字段名后调用get方法
        username = serializers.CharField()
        password = serializers.CharField()
        gp = serializers.CharField(source='group.title') # 通过点来获取值
        gp1 = serializers.CharField(source='group')
        
        # rls = serializers.CharField(source='roles.all')  # 多对多时,反向点all,返回的时Queryset列表,里面都都是对象
        rls = serializers.SerializerMethodField()  # 自定义函数获取字段
        
        def get_rls(self,row):  # 自定义函数
            role_obj_list = row.roles.all()
            ret =[]
            for item in role_obj_list:
                ret.append({'id':item.id,'title':item.title})
            return ret
        
    class UserinfoView(APIView):
        def get(self, *args, **kwargs):
            users = models.UserInfo.objects.all()
            ser = UserInfoSerializer(instance=users, many=True)
            print(ser.data)
            ret = json.dumps(ser.data, ensure_ascii=False)
            return HttpResponse(ret)
        
        #[{"xxxx": "1", "ooo": "普通用户", "username": "序号话", "password": "123", "gp": "A组", "gp1": "UserGroup object", "rls":[{"id": 1, "title": "医生"}, {"id": 2, "title": "学生"}]}, {"xxxx": "2", "ooo": "VIP", "username": "民人接", "password": "123","gp": "B组", "gp1": "UserGroup object", "rls": [{"id": 3, "title": "元昊"}]}]
    
  • # 通过多张表处理一对多字段(多对多不行)
    class Group1Serializer(serializers.Serializer):
        title = serializers.CharField(max_length=32)
    
    class UserInfoSerializer(serializers.ModelSerializer):
        group = Group1Serializer()
        class Meta:
            model = models.UserInfo
            # fields = '__all__'
            fields = ['id', 'username', 'password', 'group', 'roles']
    

# 万金油:ModelSerializer

  • # 除了上面通过写继承Serializer的类来取数据,我们还可以使用ModelSerializer来取数据,和我们的ModelForm很类似
    class UserInfoSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.UserInfo   # 选择那一张表
            fields = '__all__'  # 取出所有字段,当然也可以使用列表['id','title'...]
              
    class UserinfoView(APIView):
        def get(self, *args, **kwargs):
            users = models.UserInfo.objects.all()
            ser = UserInfoSerializer(instance=users, many=True)
            print(ser.data) 
            ret = json.dumps(ser.data, ensure_ascii=False)
            return HttpResponse(ret)
       
     # [{"id": 1, "user_type": 1, "username": "序号话", "password": "123", "group": 1, "roles": [1, 2]}, {"id": 2, "user_type": 2,"username": "民人接", "password": "123", "group": 2, "roles": [3]}]
    
  • # 方案一:fields结合source解决一对多和choices问题
    class UserInfoSerializer(serializers.ModelSerializer):  
        ooo = serializers.CharField(source='get_user_type_display') # 结合source自定义字段
        class Meta:
            model = models.UserInfo
            fields = ['id','username','ooo']  # 引入多对多字段
                
    class UserinfoView(APIView):
        def get(self, *args, **kwargs):
            users = models.UserInfo.objects.all()
            ser = UserInfoSerializer(instance=users, many=True)
            print(ser.data)
            ret = json.dumps(ser.data, ensure_ascii=False)
            return HttpResponse(ret)
        
        # [{"id": 1, "username": "序号话", "ooo": "普通用户"}, {"id": 2, "username": "民人接", "ooo": "VIP"}]
    
  • # **方案二:**通过SerializerMethodField()解决多对多问题
    class UserInfoSerializer(serializers.ModelSerializer):
        ooo = serializers.CharField(source='get_user_type_display') # 结合source自定义字段
        rls = serializers.SerializerMethodField()  # 自定义显示格式
        class Meta:
            model = models.UserInfo
            fields = ['id','username','ooo','rls']  # 引入多对多字段
            
        def get_rls(self, row):  # row就是拿到的一条数据,一个对象
            role_obj_list = row.roles.all()
            ret = []
            for item in role_obj_list:
                ret.append({'id':item.id,'tile':item.title})
            return ret
        
      # [{"id": 1, "username": "序号话", "ooo": "普通用户", "rls": [{"id": 1, "tile": "医生"}, {"id": 2, "tile": "学生"}]}, {"id": 2,"username": "民人接", "ooo": "VIP", "rls": [{"id": 3, "tile": "元昊"}]}]      
    
    • 小提示

      点开ModelSerializer,源码的第一段就是一张map表,标识了数据库中的各自字段所对应的在ModelSerualizer的字段
      
  • **方案三:**自定义类

     # 解决choices问题
    class MyField(serializers.CharField):
        def to_representation(self, value):  # value相当于我们取得某个字段
            return value.title
    
    class UserInfoSerializer(serializers.ModelSerializer):
        xl = MyField(source='group')
        class Meta:
            model = models.UserInfo
            fields = ['id','username','xl']  # 引入多对多字段
     # [{"id": 1, "username": "序号话", "xl": "A组"}, {"id": 2, "username": "民人接", "xl": "B组"}]
    
     ------------------------------------------------------------# 解决多对多问题
    class MyField(serializers.CharField):
        def to_representation(self, value):
            ret = json.dumps(list(value.all().values('id','title')),ensure_ascii=False)  # 当然我们可以再写一个serilizer序列化类,鸡肋
            return ret
    
    class UserInfoSerializer(serializers.ModelSerializer):
        # ooo = serializers.CharField(source='get_user_type_display') # 结合source自定义字段
        # rls = serializers.SerializerMethodField()  # 自定义显示格式
        xl = MyField(source='roles')
        class Meta:
            model = models.UserInfo
            fields = ['id','username','xl']  # 引入多对多字段
    
    
  • 自定义字段来进行多对多的查询

    class Animal(models.Model):
        name = models.CharField(max_length=50, default='')
        type = models.CharField(max_length=50, default='')
        country = models.ForeignKey(Country, blank=True, null=True)
    
        @property
        def country_area(self):
            return self.country.area
     
     --------------------------------------------------------
     # 在序列化类中使用
    class AnimalSerializer(serializers.Serializer):
        pk = serializers.IntegerField(read_only=True)
        name = serializers.CharField(max_length=50)
        type = serializers.CharField(max_length=50)
        country = serializers.PrimaryKeyRelatedField(read_only=True)
        country_area = serializers.FloatField(required=False, source='country_area’)
    
# 部分总结
1、写类
	1.1、继承Serializer
		class RoleSerializer(serializers.Serializer):
            id = serializers.IntegerField()
            title = serializers.CharField()  # title必须和models中的字段进行对应
   	1.2、继承ModelSerializer
   		class UserInfoSerializer(serializers.ModelSerializer):
            class Meta:
                model = models.UserInfo
                fields = ['id','username']  # '__all__'
                
2、自定义字段
	2.1、通过source:
		 a. choices字段:xxxx = serializers.CharField(source='user_type')
		 b. 一对多:gp = serializers.CharField(source='group.title')
		 
	2.2、通过SerializerMethodField()和自定义函数
		...
		def get_rls(self,row):  # 自定义函数,get_字段, row就是整张表
            role_obj_list = row.roles.all()
            ret =[]
            for item in role_obj_list:
                ret.append({'id':item.id,'title':item.title})
            return ret
        
    2.3、自定义类
# 继续学习ModelSerializer:深度控制
class UserInfoSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = '__all__'
        depth = 1  # 没写这之前,我们得到的数据都是如group,roles这种涉及到一对多和多对多的字段都是取到的pk,这之后就可以获得多个每个关联对象的所有字段
           
class UserinfoView(APIView):
    def get(self, *args, **kwargs):
        users = models.UserInfo.objects.all()
        ser = UserInfoSerializer(instance=users, many=True)
        print(ser.data) 
        ret = json.dumps(ser.data, ensure_ascii=False)
        return HttpResponse(ret)
   
 # 所有结构
    [
    {
        "id": 1,
        "user_type": 1,
        "username": "序号话",
        "password": "123",
        "group": {
            "id": 1,
            "title": "A组"
        },
        "roles": [
            {
                "id": 1,
                "title": "医生"
            },
            {
                "id": 2,
                "title": "学生"
            }
        ]
    },
    {
        "id": 2,
        "user_type": 2,
        "username": "民人接",
        "password": "123",
        "group": {
            "id": 2,
            "title": "B组"
        },
        "roles": [
            {
                "id": 3,
                "title": "元昊"
            }
        ]
    }
]
  • # 小提示
    1、depth 表示该表中如果都关系管理对象,就打开该对象的所有字段,表示深度,还可以depth=2
    2、官方建议depth=[0~10]
    
  • # 通过HyperlinkedIdentityField重写外键管理对象字段,生成动态url(必须关联一个对象)
    class UserInfoSerializer(serializers.ModelSerializer):
        group = serializers.HyperlinkedIdentityField(view_name='gp')  # 重写group,通过url别名生成路径,必须写别名,我们的url中的pk,系统默认也是取group中的pk,如果换个名字就报错了
        class Meta:
            model = models.UserInfo
            # fields = '__all__'
            fields = ['id', 'username', 'password', 'group', 'roles']
            depth = 0  #  默认为0
            
    class UserinfoView(APIView):   
        def get(self,request, *args, **kwargs):
            users = models.UserInfo.objects.all()
            ser = UserInfoSerializer(instance=users, many=True,context={'request':request})  # 需要写context
            print(ser.data)
            ret = json.dumps(ser.data, ensure_ascii=False)
            return HttpResponse(ret)
        
        
    class GourpSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.UserGroup
            fields = '__all__'
        
    class GourpView(APIView):  # 对每个组进行详细生成url
        def get(self,request, *args, **kwargs):
             pk = kwargs.get('pk')  # 通过路径传入
            obj = models.UserGroup.objects.filter(pk=pk).first()
            ser = GourpSerializer(instance=obj, many=False)
            ret = json.dumps(ser.data, ensure_ascii=False)
            return HttpResponse(ret)
        
     # url.py文件中
    urlpatterns = [
        ...
        url(r'(?P<version>[v1|v2]+)/userinfo/$',views.UserinfoView.as_view()),
        url(r'(?P<version>[v1|v2]+)/group/(?P<pk>\d+)/$',views.GourpView.as_view(),name = 'gp'),
    ]
    
     #[{"id": 1, "username": "序号话", "password": "123", "group": "http://127.0.0.1:8000/api/v1/group/1/", "roles": [1, 2]},{"id": 2, "username": "民人接", "password": "123", "group": "http://127.0.0.1:8000/api/v1/group/2/", "roles": [3]}]
    
    • # 小提示:
      1、我们url中有一个pk参数,通过HyperlinkedIdentityField字段后面的别名参数生成url时,需要参数pk,默认是一当前user.pk传入的,显然不符合我们的要求,我们的group中只有两个组,用户只继承A组
      2、对每一条数据进行序列化的时候,需要添加 ser = UserInfoSerializer(instance=users, many=True,context={'request':request})的context参数
      
  • # 升级版,修正url中的pk不对齐问题
    class UserInfoSerializer(serializers.ModelSerializer):
        group = serializers.HyperlinkedIdentityField(view_name='gp',   # 指定url别名
                                                     lookup_field='group_id', # 指定查找字段,可能是以他为参数,不能是group(对象)
                                                     lookup_url_kwarg='pk')  # 指定参数名
        class Meta:
            model = models.UserInfo
            # fields = '__all__'
            fields = ['id', 'username', 'password', 'group', 'roles']
            depth = 0
            
    class UserinfoView(APIView):
       
        def get(self,request, *args, **kwargs):
            users = models.UserInfo.objects.all()
            ser = UserInfoSerializer(instance=users, many=True,context={'request':request})  # 需要写context
            print(ser.data)
            ret = json.dumps(ser.data, ensure_ascii=False)
            return HttpResponse(ret)
        
      # [{"id": 1, "username": "序号话", "password": "123", "group1":"http://127.0.0.1:8000/api/v1/group/1/", "roles": [1, 2]}, {"id": 2, "username": "民人接", "password": "123", "group1": "http://127.0.0.1:8000/api/v1/group/2/", "roles": [3]}]
    
    • # 小提示:
      1、在序列化类中定义group1字段,其以group.id作为url中的参数出入别名为'gp'的url中,再生成json返回数据
      	group1 = serializers.HyperlinkedIdentityField(view_name='gp',source='group.id')
          
      2、源码中再HyperlinkedIdentityField中第一句:
      	look_up = 'pk'  # 表示默认以pk字段为参数传入
      	view_name = None
      

# 序列化源码解析

  • **步骤一:**代码分析

    class UserInfoSerializer(serializers.ModelSerializer):
        group = serializers.HyperlinkedIdentityField(view_name='gp',
        											lookup_field='group_id',
        											lookup_url_kwarg='pk')  
        class Meta:
            model = models.UserInfo
            # fields = '__all__'
            fields = ['id', 'username', 'password', 'group', 'roles']
            depth = 0
            
    class UserinfoView(APIView):   
        def get(self,request, *args, **kwargs):
            users = models.UserInfo.objects.all()
            ser = UserInfoSerializer(instance=users, many=True,context={'request':request}) 
            
    
            ret = json.dumps(ser.data, ensure_ascii=False)
            return HttpResponse(ret)
        
      # 以上述视图类为例子:
    1、url路径匹配带CBV,再get中对UserInfoSeriazer进行实例化,去该类中找构造函数init,所以往上走 --> ModelSerializer类,任然没有,再往上走 --> Serializer类,没有 --> BaseSerializer中找到__init__(),同时还有__new__方法,且先执行new方法,进行判断many参数
    	    def __new__(cls, *args, **kwargs):
                if kwargs.pop('many', False):  # 分析是否是一个Queryst列表
                    return cls.many_init(*args, **kwargs)
                return super().__new__(cls, *args, **kwargs)  # 对单个对象进行处理,从Field那获取空间self,然后执行自己的init方法
            def __init__(self, instance=None, data=empty, **kwargs):
                self.instance = instance   # 传入的单个对象
                if data is not empty:
                    self.initial_data = data
                self.partial = kwargs.pop('partial', False)
                self._context = kwargs.pop('context', {}) # 传入参数
                kwargs.pop('many', None)
                super().__init__(**kwargs)
    
    2、打开many_init()函数,里面返回了
    		def many_init(cls, *args, **kwargs):
            	...
            	list_serializer_class = getattr(meta, 'list_serializer_class', ListSerializer)
                #调用ListSeralizer进行处理
            	return list_serializer_class(*args, **list_kwargs)
            
    3、回到序列化后我们调用,ser.data,点击查看源码
    	3.1 @property
            def data(self):
                ret = super().data  # 调用了BaseSerializer中的data属性
                return ReturnDict(ret, serializer=self)  # 返回一个有序字典
        3.2、打开父类的data属性:(用于对_data进行返回)
        	def data(self):
                ...
        	  if not hasattr(self, '_data'):
                if self.instance is not None and not getattr(self, '_errors', None): 
                    # 我们再去choices时使用过to_repersentions,用于获取对象中的值
                    self._data = self.to_representation(self.instance)
                    ...
               return self._data
        
        3.3、查找自己的self.to_representation方法,顶层是一个抛异常,最终我们在Serializer中找到了
        	 def to_representation(self, instance):
                """
                Object instance -> Dict of primitive datatypes.
                """
                ret = OrderedDict()
                fields = self._readable_fields # 可读字段,进行了过滤了字段
    
                for field in fields: # 对我们自定义的所有字段,循环
                    try:
                        # 调用CharField.get_attribute方法,instance就是我们传入的对象
                        attribute = field.get_attribute(instance)
                    except SkipField:
                        continue
                  check_for_none = attribute.pk if isinstance(attribute, PKOnlyObject) 
                															else attribute
                if check_for_none is None:
                    ret[field.field_name] = None
                else:
                    ret[field.field_name] = field.to_representation(attribute)
            return ret
        
        3.4、找到父父父类Field中的get_attrbute(self,instance)
        	 def get_attribute(self, instance):
                """
                Given the *outgoing* object instance, return the primitive value
                that should be used for this field.
                """
                try:
                    return get_attribute(instance, self.source_attrs) # 这里的source就是我们传入的source进行切割的列表,如'group.id'->[group,id], 我们再点开get_attrbute方法
                ...
        3.5、在进入更内层的get_attribute()
        	  def get_attribute(instance, attrs):
                    """
                    Similar to Python's built in `getattr(instance, attr)`,
                    but takes a list of nested attributes, instead of a single attribute.
                    Also accepts either attribute lookup on objects or dictionary lookups.
                    """
                    # 循环传入的source列表[group,title],或则['get_user_type_display']
                    for attr in attrs:
                        try:
                            if isinstance(instance, Mapping):
                                instance = instance[attr]
                            else:
                                instance = getattr(instance, attr) # 拿到一个对象字段,给instance
                        except ObjectDoesNotExist:
                            return None
                        if is_simple_callable(instance): # 判断是否是可以调用的如get_user_type_display
                            try:
                                instance = instance() # 执行函数
                     	...
                    return instance
    
    • # 小提示:
      1、 many=True,将QuerySet列表交给ListSerializer处理
      	many=False ,将对象交给Serliazer处理
      
      2、单个对象:self.to_representation
         Queryset时:self.to_representation
      
      

# 序列化请求数据校验

  • # 简单的从请求体中获取数据,并打印出form中的数据校验
    class UserGroupSerializer(serializers.Serializer):
        titile = serializers.CharField(error_messages={'required':'标题不能为空'}) # 因为继承了Field,所以都有
        
    class UserGourpView(APIView):
        def post(self,request, *args, **kwargs):
            print(request.body)  # 必须再data之前使用,否则data后就没有body了,b'{\n"name":"alex"\n}'
            print(request.data) # 已经配置了解析器 {'name': 'alex'}
            ser = UserGroupSerializer(data=request.data)
            if ser.is_valid():
                print(ser.validated_data)
            else:
                print(ser.errors)
            return HttpResponse('提交数据')
      
     # url中
    urlpatterns = [
    	...
        url(r'(?P<version>[v1|v2]+)/usergroup/$',views.UserGourpView.as_view()),
    ]
    
     # 运行结果
     {'titile': [ErrorDetail(string='标题不能为空', code='required')]}
    
  • # 自定义类进行内置校验器校验
    class XXXValidator(object):
        def __init__(self, base):
            self.base = base  # 传入的参数
    
        def __call__(self, value): # 只要触犯实例化就会触发__call__方法
            if not value.startswith(self.base):  # 必须以base开头
                message = '标题必须以 %s 开头.' % self.base
                raise serializers.ValidationError(message)
    
        def set_context(self, serializer_field):
            """
            This hook is called by the serializer instance,
            prior to the validation call being made.
            """
            # 执行验证之前调用,serializer_fields是当前字段对象
            pass
    
    class UserGroupSerializer(serializers.Serializer):
        title = serializers.CharField(error_messages={'required':'标题不能为空'},
        												validators=[XXXValidator('中国'),])
        
    class UserGourpView(APIView):
        def post(self,request, *args, **kwargs):
            print(request.body)  # 必须再data之前使用,否则data后就没有body了,b'{\n"name":"alex"\n}'
            print(request.data) # 已经配置了解析器 {'name': 'alex'}
            ser = UserGroupSerializer(data=request.data)
            if ser.is_valid():
                print(ser.validated_data)
            else:
                print(ser.errors)
            return HttpResponse('提交数据')
    
    • 补充:

      当然可以通过函数进行内置校验器校验:
      def  validate_title(value):
          if 'mjj' in value:
              raise serializers.ValidationError
          
      class UserGroupSerializer(serializers.Serializer):
          title = serializers.CharField(error_messages={'required':'标题不能为空'},validators=[validate_title,])
      
  • # 序列化的局部校验(钩子函数)
    class UserGroupSerializer(serializers.Serializer):
        title = serializers.CharField(error_messages={'required':'标题不能为空'},validators=[])
        
        def validate_title(self,value):  # validate_字段名,用来局部验证,局部钩子
            if 'mjj' in value:
                raise serializers.ValidationError
            return value
        
    class UserGourpView(APIView):
        def post(self,request, *args, **kwargs):
            print(request.body)  # 必须再data之前使用,否则data后就没有body了,b'{\n"name":"alex"\n}'
            print(request.data) # 已经配置了解析器 {'name': 'alex'}
            ser = UserGroupSerializer(data=request.data)
            if ser.is_valid():
                print(ser.validated_data)
            else:
                print(ser.errors)
            return HttpResponse('提交数据')
    
  • # 序列化的全局钩子
    
    class UserGroupSerializer(serializers.Serializer):
        title = serializers.CharField(error_messages={'required':'标题不能为空'},validators=[])    
        def validate_title(self,value):  # validate_字段名,用来局部验证
            if 'mjj' in value:
                raise serializers.ValidationError
            return value
        
        def validate(self, attrs):
            print(attrs)  # OrderedDict([('title', 'mj')])
            if attrs['title'] == '666':
                raise serializers.ValidationError  #{'non_field_errors': [ErrorDetail(string='Invalid input.', code='invalid')]}
            return attrs
        
    class UserGourpView(APIView):
        def post(self,request, *args, **kwargs):
            print(request.body)  # 必须再data之前使用,否则data后就没有body了,b'{\n"name":"alex"\n}'
            print(request.data) # 已经配置了解析器 {'name': 'alex'}
            ser = UserGroupSerializer(data=request.data)
            if ser.is_valid():
                print(ser.validated_data)
            else:
                print(ser.errors)
            return HttpResponse('提交数据')
    
  • # 使用extra_kwargs进行每个字段的扩展
    class PasswordValidator(object):
        def __init__(self, base):
            self.base = str(base)
    
        def __call__(self, value):
            if value != self.base:
                message = 'This field must be %s.' % self.base
                raise serializers.ValidationError(message)
    
        def set_context(self, serializer_field):
            """
            This hook is called by the serializer instance,
            prior to the validation call being made.
            """
            # 执行验证之前调用,serializer_fields是当前字段对象
            pass
    
    
    class ModelUserSerializer(serializers.HyperlinkedModelSerializer):
        ll = serializers.HyperlinkedIdentityField(view_name='xxxx')
        tt = serializers.CharField(required=False)
    
        class Meta:
            model = models.UserInfo
            fields = "__all__"
            list_serializer_class = serializers.ListSerializer
    
            extra_kwargs = {  # 可以对每个字段进行配置,甚至read_only
                'user': {'min_length': 6},
                'pwd': {'validators': [PasswordValidator(666), ]},
                'url': {'view_name': 'xxxx'},
                'ut': {'view_name': 'xxxx'},
            }
    
    
    class TestView(APIView):
        def get(self, request, *args, **kwargs):
            # # 序列化,将数据库查询字段序列化为字典
            data_list = models.UserInfo.objects.all()
            ser = ModelUserSerializer(instance=data_list, many=True, context={'request': request})
            return Response(ser.data)
    
        def post(self, request, *args, **kwargs):
            # 验证,对请求发来的数据进行验证
            print(request.data)
            ser = ModelUserSerializer(data=request.data)
            if ser.is_valid():
                print(ser.validated_data)
            else:
                print(ser.errors)
            return Response('POST请求,响应内容')